<!DOCTYPE html>
<html lang="en" x-data="{ darkMode: false }" :class="{ 'dark': darkMode }" class="bg-slate-100">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="{{site_description|default('RustPBX Admin Console')}}" />
    <title>{% block title %}{{page_title|default('RustPBX Admin')}}{% endblock %}</title>
    {% block js_head %}
    {% for js in js_files %}
    <script src="{{ js }}" defer></script>
    {% endfor %}
    <script defer src="{{alpine_js|default('https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js')}}"></script>
    <script src="{{tailwind_js|default('https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4')}}"></script>
    {% block js_ext %}
    {% endblock %}
    <script>
        document.addEventListener('alpine:init', () => {
            const STORAGE_KEYS = {
                sidebarCollapsed: 'console.sidebar.collapsed',
            };

            Alpine.data('consoleLayoutShell', () => ({
                sidebarOpen: false,
                sidebarCollapsed: false,
                init() {
                    this.sidebarCollapsed = this.readCollapsedState();
                },
                readCollapsedState() {
                    try {
                        const raw = window.localStorage.getItem(STORAGE_KEYS.sidebarCollapsed);
                        return raw === 'true';
                    } catch (_) {
                        return false;
                    }
                },
                toggleSidebarCollapse() {
                    this.sidebarCollapsed = !this.sidebarCollapsed;
                    this.persistCollapsedState();
                },
                persistCollapsedState() {
                    try {
                        window.localStorage.setItem(
                            STORAGE_KEYS.sidebarCollapsed,
                            this.sidebarCollapsed ? 'true' : 'false',
                        );
                    } catch (_) {
                        /* ignore storage errors */
                    }
                },
            }));

            Alpine.data('consoleConfirmDialog', () => ({
                open: false,
                title: 'Are you sure?',
                message: '',
                confirmLabel: 'Confirm',
                cancelLabel: 'Cancel',
                destructive: false,
                onConfirm: null,
                init() {
                    window.addEventListener('console:confirm', (event) => {
                        const detail = event.detail || {};
                        this.title = detail.title || 'Are you sure?';
                        this.message = detail.message || detail.description || '';
                        this.confirmLabel = detail.confirmLabel || 'Confirm';
                        this.cancelLabel = detail.cancelLabel || 'Cancel';
                        this.destructive = Boolean(detail.destructive);
                        this.onConfirm = typeof detail.onConfirm === 'function' ? detail.onConfirm : null;
                        this.open = true;
                        this.$nextTick(() => {
                            this.$refs.confirmButton?.focus();
                        });
                    });
                },
                close() {
                    this.open = false;
                    this.onConfirm = null;
                },
                confirm() {
                    if (typeof this.onConfirm === 'function') {
                        const result = this.onConfirm();
                        if (result && typeof result.then === 'function') {
                            result.finally(() => this.close());
                            return;
                        }
                    }
                    this.close();
                },
            }));
        });
    </script>
    {% endblock %}
    <style>
        [x-cloak] {
            display: none !important;
        }

        input::placeholder,
        textarea::placeholder {
            color: rgba(148, 163, 184, 0.7);
            opacity: 1;
        }

        .dark input::placeholder,
        .dark textarea::placeholder {
            color: rgba(148, 163, 184, 0.6);
        }
    </style>
</head>

<body class="min-h-screen bg-slate-100 text-slate-900">
    <div x-data="consoleLayoutShell()" x-init="init()" class="min-h-screen">
        <!-- Off-canvas overlay -->
        <div x-show="sidebarOpen" x-transition.opacity class="fixed inset-0 z-30 bg-black/40 lg:hidden"
            @click="sidebarOpen=false"></div>

        <!-- Sidebar -->
        <aside
            class="fixed inset-y-0 left-0 z-40 w-72 transform bg-white ring-1 ring-black/5 shadow-xl transition-all duration-300 ease-in-out lg:translate-x-0 lg:shadow-none lg:ring-0"
            :class="{ '-translate-x-full': !sidebarOpen, 'lg:w-24': sidebarCollapsed, 'lg:w-72': !sidebarCollapsed }"
            x-transition>
            <div class="flex h-full flex-col">
                <div class="flex h-16 items-center justify-between gap-2 border-b border-slate-100 px-6">
                    <div class="flex items-center gap-2">
                        <div class="h-8 w-8 rounded-lg bg-sky-600 text-white grid place-content-center font-bold">R
                        </div>
                        <div class="font-semibold" x-show="!sidebarCollapsed" x-transition.opacity x-cloak>RustPBX</div>
                    </div>
                    <button class="hidden rounded-lg p-2 hover:bg-slate-100 lg:inline-flex"
                        @click="toggleSidebarCollapse()" aria-label="Toggle sidebar">
                        <svg x-show="!sidebarCollapsed" x-transition.opacity class="h-5 w-5 text-slate-600"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M15 5l-7 7 7 7" />
                        </svg>
                        <svg x-show="sidebarCollapsed" x-transition.opacity class="h-5 w-5 text-slate-600"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
                        </svg>
                    </button>
                </div>
                <!-- Nav -->
                <nav class="flex-1 overflow-y-auto px-3 py-4 text-sm transition-all duration-300"
                    :class="{ 'px-2': sidebarCollapsed }">
                    {% set bp = base_path | default('/console') %}
                    <a href="{{ bp }}/"
                        class="group flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='dashboard' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Dashboard">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='dashboard' else 'text-slate-400 group-hover:text-slate-600' }}"
                            fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Dashboard</span>
                    </a>
                    <a href="{{ bp }}/extensions"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='extensions' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Extensions">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='extensions' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Extensions</span>
                    </a>
                    <a href="{{ bp }}/routing"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='routing' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Routing">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='routing' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Routing</span>
                    </a>
                    <a href="{{ bp }}/sip-trunk"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active in ['sip-trunk', 'sip-trunk-detail'] else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="SIP Trunk / DID">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active in ['sip-trunk', 'sip-trunk-detail'] else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>SIP Trunk / DID</span>
                    </a>
                    <a href="{{ bp }}/bill-templates"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='bill-templates' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Billing templates">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='bill-templates' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M7.5 3h9a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25h-9A2.25 2.25 0 0 1 5.25 18V5.25A2.25 2.25 0 0 1 7.5 3zm0 4.5h9m-9 4.5h9m-9 4.5h4.5" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Billing templates</span>
                    </a>
                    <a href="{{ bp }}/call-records"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='call-records' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Call Records">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='call-records' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Call Records</span>
                    </a>
                    <a href="{{ bp }}/diagnostics"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='diagnostics' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Diagnostics">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='diagnostics' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0 1 12 15a9.065 9.065 0 0 0-6.23-.693L5 14.5m14.8.8 1.402 1.402c1.232 1.232.65 3.318-1.067 3.611A48.309 48.309 0 0 1 12 21c-2.773 0-5.491-.235-8.135-.687-1.718-.293-2.3-2.379-1.067-3.61L5 14.5" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Diagnostics</span>
                    </a>
                    <a href="{{ bp }}/settings"
                        class="group mt-1 flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-slate-50 transition-all duration-200 {{ 'bg-sky-50 text-sky-700' if nav_active=='settings' else 'text-slate-700' }}"
                        :class="{ 'justify-center gap-0 px-2 py-3': sidebarCollapsed }" title="Settings">
                        <svg class="h-5 w-5 flex-shrink-0 {{ 'text-sky-600' if nav_active=='settings' else 'text-slate-400 group-hover:text-slate-600' }}"
                            viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" />
                        </svg>
                        <span x-show="!sidebarCollapsed" x-transition.opacity x-cloak>Settings</span>
                    </a>
                </nav>
                <div class="border-t border-slate-100 p-4 text-xs text-slate-500" x-show="!sidebarCollapsed"
                    x-transition.opacity x-cloak>
                    <a :href='"https://rustpbx.com?version={{site_version}}&utm_source="+location.origin'
                        target="_blank" class="underline hover:text-slate-700">{{site_name|default('RustPBX')}} -
                        {{site_version}}</a>
                </div>
            </div>
        </aside>

        <!-- Main area -->
        <div class="transition-all duration-300 ease-in-out" x-bind:class="sidebarCollapsed ? 'lg:pl-24' : 'lg:pl-72'">
            <!-- Top bar -->
            <header
                class="sticky top-0 z-20 flex h-16 items-center justify-between border-b border-slate-200 bg-white/80 px-6 backdrop-blur">
                <div class="flex items-center gap-2">
                    <button class="rounded-lg p-2 hover:bg-slate-100 lg:hidden" @click="sidebarOpen = true"
                        aria-label="Open sidebar">
                        <svg class="h-5 w-5 text-slate-700" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                            stroke-width="2">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
                        </svg>
                    </button>
                </div>
                <div class="flex items-center gap-3">
                    <button class="rounded-lg p-2 hover:bg-slate-100" title="Notifications">
                        <svg class="h-5 w-5 text-slate-600" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                            stroke-width="1.8">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6 6 0 10-12 0v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
                        </svg>
                    </button>
                    {% if logout_url %}
                    <a href="{{ logout_url }}"
                        class="rounded-lg bg-rose-500 px-3 py-1.5 text-xs font-semibold text-white hover:bg-rose-400">Sign
                        out</a>
                    {% endif %}
                </div>
            </header>

            <main class="min-h-[calc(100vh-64px)] bg-slate-50">
                {% block content %}{% endblock %}
            </main>

            <footer class="py-6 text-center text-xs text-slate-500">
                {{site_name|default('RustPBX')}} · {{site_footer}}
            </footer>
        </div>
    </div>

    <div x-data="consoleConfirmDialog()" x-show="open" x-cloak x-transition.opacity.duration.150ms
        class="fixed inset-0 z-[100] flex items-center justify-center">
        <div class="absolute inset-0 bg-slate-900/50 backdrop-blur-sm" @click="close" aria-hidden="true"></div>
        <div class="relative z-10 w-full max-w-sm origin-center scale-95 transform rounded-xl bg-white p-6 shadow-2xl ring-1 ring-black/10"
            x-transition.scale.duration.150ms @keydown.escape.window="close">
            <div class="flex items-start gap-3">
                <div class="mt-1 flex h-10 w-10 items-center justify-center rounded-full"
                    :class="destructive ? 'bg-rose-100 text-rose-600 ring-1 ring-rose-200' : 'bg-sky-100 text-sky-600 ring-1 ring-sky-200'">
                    <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                        <path stroke-linecap="round" stroke-linejoin="round"
                            d="M12 9v4m0 4h.01M5.25 7.5h13.5L18 19.5H6l-.75-12z" />
                    </svg>
                </div>
                <div class="flex-1 space-y-2">
                    <h2 class="text-base font-semibold text-slate-900" x-text="title"></h2>
                    <p class="text-sm text-slate-600" x-show="message" x-text="message"></p>
                </div>
            </div>
            <div class="mt-6 flex items-center justify-end gap-2">
                <button type="button"
                    class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-slate-300 focus:ring-offset-2"
                    @click="close" x-text="cancelLabel"></button>
                <button type="button" x-ref="confirmButton"
                    class="inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-semibold text-white shadow-sm transition focus:outline-none focus:ring-2 focus:ring-offset-2"
                    :class="destructive ? 'bg-rose-600 hover:bg-rose-500 focus:ring-rose-500' : 'bg-sky-600 hover:bg-sky-500 focus:ring-sky-500'"
                    @click="confirm" x-text="confirmLabel"></button>
            </div>
        </div>
    </div>

</body>

</html>